# -*- coding: utf-8 -*-

# Copyright (C) 2017 Intel Corporation.  All rights reserved.
# Copyright (c) WanSheng Intelligent Corp. All rights reserved.

#
# Licensed under the Apache License, Version 2.0 (the "License");
#
import socket
import logging

from threading import Thread

from coapthon import defines
from coapthon.server.coap import CoAP
from coapthon.resources.resource import Resource

from wa_edge_iot.internals.rd_parser import RDParser

from wa_edge_iot.framework.data_process import DataProcess
from wa_edge_iot.framework.rd_query_param import find_query_key
from wa_edge_iot.framework.handler_base import CoAPHandlersBase

from wa_edge_iot.model.resource_data_base import ResourceDataBase
from wa_edge_iot.model.constants import MediaTypeFormat

logger = logging.getLogger(__name__)


def find_listen_port():
    s = None
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.bind(('', 0))
        addr, port = s.getsockname()
        return port
    except Exception as e:
        return 9981
    finally:
        if s != None:
            s.close()

def parse_dp_query(query):
    di = None
    ri = None
    pt = None
    tag = ""
    for item in query.split('&'):
        k = item.split("=")
        if len(k) == 2:
            if k[0] == 'di':
                di = k[1]
            elif k[0] == 'ri':
                ri = k[1]
            elif k[0] == 'tag':
                tag = k[1]
            elif k[0] == 'pt':
                pt = k[1]
    return di, ri, pt, tag



class SimpleRdMonitorResource(Resource):
    def __init__(self, name="SimpleRdMonitorResource", user_callback=None):
        super(SimpleRdMonitorResource, self).__init__(name)
        assert isinstance(user_callback, CoAPHandlersBase)
        self.__user_callback = user_callback

    def set_user_handler(self, callback):
        self.__user_callback = callback

    def render_PUT_advanced(self, request, response):
        from coapthon.messages.response import Response
        assert(isinstance(response, Response))
        query = request.uri_query
        tag = find_query_key(query, "tag")
        
        fmt = request.content_type
        
        devices = RDParser.parse_device(str(request.payload))
            
        self.__user_callback.on_monitored_device_change(
            devices, 
            tag)  
        
        return self, None
        

                
class SimpleDataMonitorResource(Resource):
    def __init__(self, name="SimpleDataMonitorResource", user_callback=None):
        super(SimpleDataMonitorResource, self).__init__(name)
        assert isinstance(user_callback, CoAPHandlersBase)
        self.__user_callback = user_callback

    def set_user_handler(self, callback):
        self.__user_callback = callback

    def render_POST_advanced(self, request, response, is_put = False):
        from coapthon.messages.response import Response
        assert(isinstance(response, Response))
        fmt = request.content_type
        # URI QUERY FORMAT: di={}&ri={}&pt={}&tag={}
        query = request.uri_query
        logger.info( "DM POST: query is {}, uri is {}, is put: {}".format( query, request.uri_path, is_put))
        device_id, resource_uri, pt, tag = parse_dp_query(query)
        if not device_id:
            logger.warn("device_id not in query: " + query)
            return self, None
        if not resource_uri:
            logger.warn( "resource_uri not in query: " + query)
            return self, None

        
        if request.payload is None or len(request.payload) == 0:
            return self, None
        
        from wa_edge_iot.framework.wagent import I_wagent
        parser = I_wagent().data_util().get_parser(fmt)
        if parser is None:
            data = ResourceDataBase(request.payload, request.content_type)
            # simple add the plain text content for a property
            if request.content_type == MediaTypeFormat.TEXT_PLAIN and pt:
                data.set_property_value(pt, request.payload)
        else:
            data = parser.parse(request.payload, request.content_type)
            
        if data is None:
            logger.warn( "data parse failed")
            return self, None
        
        if is_put:
            self.__user_callback.on_resource_data_process(
                device_id, 
                resource_uri,
                data, 
                tag, 
                None)              
            return self, None
        
        data_process = DataProcess(fmt)
        self.__user_callback.on_resource_data_process(
            device_id, 
            resource_uri,
            data, 
            tag, 
            data_process)  
        
    
        if data_process.get_decision() == DataProcess.CONTINUE:
            response.code = defines.Codes.CONTINUE.number
        elif data_process.get_decision() == DataProcess.DROP:
            response.code = defines.Codes.FORBIDDEN.number
        elif data_process.get_decision() == DataProcess.MODIFY:
            response.code = defines.Codes.CHANGED.number
            response.payload, content_type = data_process.get_modified_data()
        else:
            response.code = defines.Codes.CONTINUE.number
            
        return self, response

    
    def render_PUT_advanced(self, request, response):
        from coapthon.messages.response import Response
        assert(isinstance(response, Response))
        fmt = request.content_type
        
        return self.render_POST_advanced(request, response, True)
        
            
class MyCoapServer(Thread):
    def __init__(self, coap_server):
        Thread.__init__(self)
        self.__coap_server = None
        self.__server_started = False
        self.__coap_server = coap_server
        
    def is_started(self):
        return self.__server_started
    
    def get_coap_server(self):
        return self.__coap_server

    def start_server(self):
        if self.__server_started:
            return
        
        self.start()
        self.__server_started = True

    def stop_server(self):
        self.__coap_server.close()
        self.__stared = False

    # the entry of thread start()???
    def run(self):
        try:
            self.__coap_server.listen(10)       
        except:
            self.__coap_server.close()
            logger.error( " server close by error")

